<template>
  <div class="pageUploader">
    <!-- 分片上传 -->
    <div class="main">
        <uploader
            ref="uploader"
            :options="options"
            :file-status-text="fileStatusText"
            :autoStart="false"
            @file-added="onFileAdded"
            @file-progress="onFileProgress"
            @file-success="onFileSuccess"
            @file-error="onFileError"
            class="uploader">
            <uploader-unsupport></uploader-unsupport>
            <uploader-drop>
                <div class="btns">
                    <uploader-btn class="upfile" :single="multiple" ><i class="iconfont icon-upload"></i> 上传文件</uploader-btn>
                </div>
                <!-- <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> -->
            </uploader-drop>
            <!-- <uploader-list ref="uploadList"></uploader-list> -->
            <uploader-list>
                <div class="file-panel" slot-scope="props">
                    <ul class="file-list">
                        <li v-for="file in props.fileList" :key="file.id" class="liLine">
                            <uploader-file class="contentFile" ref="files" :file="file" :list="true"></uploader-file>
                            <div class="line" @click="remove(file)">删除</div>
                            <div class="line" @click="download(file)">下载</div>
                        </li>
                        <div class="no-file" v-if="!props.fileList.length"><i class="nucfont inuc-empty-file"></i> 暂无待上传文件</div>
                    </ul>
                </div>
            </uploader-list>
        </uploader>
    </div>
  </div>
</template>

<script lang="ts">
import { excelBlob } from "@/utils/auth"
import { confirm } from "@/utils/popup"
import { base_Url } from "@/config"
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import request from "@/utils/request"
import SparkMD5 from 'spark-md5';
import { getCookie } from '@/utils/cookie';
import popup from "@/components/system/popup/popup.vue"
@Component({
    components:{
        popup
    }
})
export default class extends Vue{
    @Prop({ default: "mainFile" }) mainType!:string  // 附件唯一key
    @Prop({ default: "4d5abdf4be35435398a4a80341fd774d" }) mainId!:string // 指定主文档id
    @Prop({ default:()=> [] }) enableFiles!:any[] // 限制上传文件的类型
    @Prop({ default: 20 * 1024 * 1024 }) fileSingleSizeLimit!:number // 上传大小限制
    @Prop({ default: 1 * 1024 * 1024 }) fileChunkSize!:number // 分片大小
    @Prop({ default: 1 }) fileNumLimit!:number // 限制上传文件的个数
    @Prop({ default: 'sys/att/upload.do'}) url!: string // 上传地址
    @Prop({ default:'文件上传' }) title!:string // 弹窗文字
    @Prop({ default:false }) show !:boolean // 控制弹窗的显示和隐藏
    // @Prop({}) complete!:Function // 保存函数
    @Prop({ default:'' }) filePath!: string
    @Prop({ default:true }) multiple !:boolean // 是否多选
    @Prop({ default:()=> [] }) echoList!:any[] // 回显列表
    public fileList:any = []
    public selectId:any = []
    public uploaderInstance:any = null
    public file:any = {}
    public chunks:number = 0
    public fileArr:any = []
    public temporary:any[] = []
    public echo:any = []
    public options =  {
        target: base_Url + this.url,
        chunkSize: this.fileChunkSize,  //2MB
        simultaneousUploads: 3, //并发上传数
        headers: {
            'bwTicket': getCookie('token'),
            'timestamp': getCookie('timestamp'),
            'nonce':getCookie('nonce'),
            'signature':getCookie('signature'),
        },
        maxChunkRetries: 2, //最大自动失败重试上传次数
        parseTimeRemaining: function (timeRemaining:any, parsedTimeRemaining:any) { //格式化时间
            return parsedTimeRemaining
                .replace(/\syears?/, '年')
                .replace(/\days?/, '天')
                .replace(/\shours?/, '小时')
                .replace(/\sminutes?/, '分钟')
                .replace(/\sseconds?/, '秒')
        },
        testChunks: true,   //开启服务端分片校验
        // 服务器分片校验函数
        checkChunkUploadedByResponse: (chunk:any, message:any) => {
            return false
        },
    }

    public statusTextMap:any =  {
        success: '上传成功',
        error: '上传出错了',
        uploading: '上传中...',
        paused: '暂停',
        waiting: '等待中...',
        cmd5: '计算md5...'
    }

    private fileStatusText =  (status:any, response:any) => {
        return this.statusTextMap[status];
    }

    @Watch('uploaderInstance.fileList',{ deep:true })
    private fileListUpdate(n:any,o:any){
        this.fileArr = n
    }

    @Watch('echoList',{ deep:true })
    private listUpdate(n:any,o:any){
        // 监听回显列表
        this.echo = n
        this.echo.forEach((v:any,i:number)=>{
            v.fileId = 'item'+ i
        })
    }

    private onFileAdded(file:any) {
        this.fileArr = this.uploaderInstance.fileList;
        if(this.fileArr.length == 0){
            this.temporary.push(file)
            this.fileArr = this.temporary
        }else{
            this.temporary = []
        }
        // 校验文件类型
        const fileType = file.name.split('.').pop().toLowerCase()
        if(this.enableFiles.length>0){
            if(!this.enableFiles.includes(fileType)){
                this.$message.error('文件不符合上传类型')
                this.uploaderInstance.fileRemoved(file,true)
                return false
            }
        }
        // 校验文件数量
        if(this.fileArr.length> this.fileNumLimit){
            this.$message.error('文件数量超出限制')
            this.uploaderInstance.fileRemoved(file,true)
            return false
        }
        // 校验文件大小
        if(file.size> this.fileSingleSizeLimit){
            this.$message.error('文件超出限制大小')
            this.uploaderInstance.fileRemoved(file,true)
            return false
        }
        // 计算MD5
        this.computeMD5(file);
    }

        //计算MD5
    private computeMD5(file:any) {
        let blobSlice = (File as any).prototype.slice || (File as any).prototype.mozSlice  || (File as any).prototype.webkitSlice,
            chunkSize = 2097152,
            chunks = Math.ceil(file.size / chunkSize),
            currentChunk = 0,
            spark = new SparkMD5.ArrayBuffer(),
            fileReader = new FileReader();

        let time = new Date().getTime();

        file.cmd5 = true;

        fileReader.onload = (e:any) => {
            spark.append(e.target.result);   // Append array buffer
            currentChunk++;

            if (currentChunk < chunks) {
                //console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1} / ${chunks}分片解析`);
                let percent = Math.floor(currentChunk / chunks * 100);
                file.cmd5progress = percent;
                loadNext();
            } else {
                let md5 = spark.end();
                // console.log(`MD5计算完成：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`);
                spark.destroy(); //释放缓存
                file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
                file.cmd5 = false; //取消计算md5状态
                this.chunks = chunks
                file.filePath = this.filePath;
                this.file = file
                file.resume(); //开始上传
            }
        };

        fileReader.onprogress = ()=>{
            // 将参数绑定到file上，以便于删除时查询
            file.successUploader = this.fileList
        }

        fileReader.onerror = () => {
            console.warn('oops, something went wrong.');
            file.cancel();
        };

        let loadNext = () =>　{
            let start = currentChunk * chunkSize,
                end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

            fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
        };

        loadNext();
    }
    // 文件进度的回调
    private onFileProgress(rootFile:any, file:any, chunk:any) {
        // console.log(`上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`,rootFile)
    }
    // 上传成功事件
    private onFileSuccess(rootFile:any, file:any, response:any, chunk:any) {
        let resp = JSON.parse(response);
        //合并分片
        if (resp.code == 0) {
            let model = new FormData()
            model.append('action','3')
            model.append('mainId', this.mainId)
            model.append('mainType',this.mainType)
            model.append('md5',this.file.uniqueIdentifier)
            model.append('fileName',this.file.name)
            model.append('filePath',file.gyhFilePath || '')
            model.append('fileSize',this.file.size)
            model.append('chunks',String(this.chunks))
            request({
                url:this.options.target,
                method:'post',
                data: model
            }).then(res=>{
                res.data.fileName = this.file.name
                res.data.fileId = this.file.id
                res.data.gyhFilePath = file.gyhFilePath
                this.fileList.push(res)
                this.save()
            })
        }else{
            this.$message.error('上传失败')
        }
    }

    private download(file:any){
      // 下载事件
      let sliceUrl = JSON.parse(JSON.stringify(this.url))
      let sliceBy = sliceUrl.split('/')
      sliceBy.pop()
      let urlBy = sliceBy.join('/')
      const url = base_Url + urlBy + '/download.do'
      const model = {
        id: file.successUploader[0].data.attId
      }
      request({
        url:url,
        method:'post',
        data: model,
        responseType:'blob'
      }).then((res:any)=>{
        excelBlob(res)
      })
    }

    private remove(file:any){
      // 删除事件
      this.uploaderInstance.fileRemoved(file,true)
      let sliceUrl = JSON.parse(JSON.stringify(this.url))
      let sliceBy = sliceUrl.split('/')
      sliceBy.pop()
      let urlBy = sliceBy.join('/')
      const url = base_Url + urlBy + '/delete.do'
      const model = {
        id: file.successUploader[0].data.attId
      }
      request({
        url:url,
        method:'post',
        data: model
      }).then((res:any)=>{
        this.save()
      })
    }

    private onFileError(rootFile:any, file:any, response:any, chunk:any) {
        // console.log('Error:', response)
    }


    private save(){
        // 保存事件
        for(let i=this.fileList.length-1;i>=0;i--){
          if(this.uploaderInstance.fileList.length == 0){
             this.fileList = [] 
          }else{
              this.uploaderInstance.fileList.forEach((v:any)=>{
                if(this.fileList[i].data.gyhFilePath != v.gyhFilePath){
                    this.fileList.splice(i,1)
                }
              })
          }
        }
        this.$emit('complete',this.fileList)
    }


    created(){
        this.echo = this.echoList
        // this.echo.forEach((v:any,i:number)=>{
        //     v.fileId = 'item'+ i
        // })
    }

    mounted(){
        this.$nextTick(()=>{
            this.uploaderInstance = (this.$refs as any).uploader
        })
    }
}
</script>

<style scoped lang="scss">
.pageUploader{
  min-width: 300px;
  max-width: 100%;
}
::v-deep .uploader-drop{
    border: none;
    // border-bottom: 1px dashed #ccc;
}
.btns{
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

::v-deep .uploader-file-actions .uploader-file-remove{
  // 隐藏删除图标
  display: none;
}

::v-deep .uploader-file-info .uploader-file-name .uploader-file-icon{
  // 隐藏图标
  display: none;
}

::v-deep .uploader-file-info .uploader-file-size{
  // 隐藏文件大小
  display: none;
}
::v-deep .uploader-file-info .uploader-file-status{
  // 隐藏文件状态
  display: none;
}

.file-panel::-webkit-scrollbar {
    width: 4px;    
    /*height: 4px;*/
}
.file-panel::-webkit-scrollbar-thumb {
    border-radius: 10px;
    box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
    background: rgba(0,0,0,0.2);
}
.file-panel::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
    border-radius: 0;
    background: rgba(0,0,0,0.1);

}
.file-panel{
    width: 100%;
    max-height: 400px;
    overflow: auto;
    .file-list{
        width: 100%;
        height: auto;
        padding: 5px 0px 0px 6px;
        box-sizing: border-box;
        .custom{
            width: 100%;
            height: 50px;
            display: flex;
            align-items: center;
            border-bottom: 1px solid #ccc;
            .check{
                width: 20px;
                height: 20px;
                border: 1px solid #ccc;
                border-radius: 3px;
                margin-right: 10px;
                cursor: pointer;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
            }
            .active{
                background: rgb(64,158,255);
                border: 1px solid transparent;
            }
            .contentFile{
                flex:1;
                display: flex;
                align-items: center;
                .text{
                    width: 45%;
                    overflow: hidden;
                    white-space: nowrap;
                    display: flex;
                    align-items: center;
                    text-overflow: ellipsis;
                    text-indent: 14px;
                    .logo{
                    width: 30px;
                    height: 30px;
                    display: flex;
                    margin: 0px 10px 0px 5px;
                    align-items: center;
                    justify-content: center;
                    i{
                        font-size: 28px;
                    }
                }
                }
                .meta{
                    width: 8%;
                }
                .size{
                    width: 13%;
                    text-indent: 10px;
                }
                .suc{
                    width: 24%;
                    text-indent: 20px;
                }
                .close{
                    width: 10%;
                    font-size: 16px;
                    color: gray;
                    cursor: pointer;
                    i{
                        font-weight: bold;
                    }
                }
            }
        }
        .no-file{
            width: 100%;
            text-align: center;
            padding: 5px 0px;
            font-size: 14px;
            color: #ccc;
        }
        .liLine{
            display: flex;
            align-items: center;
            width: 100%;
            .line{
              font-size: 14px;
              cursor: pointer;
              margin-left: 10px;
            }
            .check{
                width: 20px;
                height: 20px;
                border: 1px solid #ccc;
                border-radius: 3px;
                margin-right: 10px;
                cursor: pointer;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
            }
            .active{
                background: rgb(64,158,255);
                border: 1px solid transparent;
            }
            .contentFile{
                flex:1;
            }
        }
    }
}
::v-deep .uploader-file{
  border-bottom: none;
}
::v-deep .uploader-dro{
  background: white;
}
::v-deep .uploader-file .uploader-file-progress{
  background:white;
}
::v-deep .uploader-file[status=success] .uploader-file-remove{
    display: none;
    
}

.uploader {
    width: 100%;
    padding: 15px;
    margin: 20px auto 0;
    font-size: 14px;
    .uploader-btn {
        margin-right: 4px;
        color: #fff;
        padding: 6px 16px;
    }
    .upfile{
        border: 1px solid #409eff;
        background: #409eff;
    }
    .updir{
        border: 1px solid #67c23a;
        background: #67c23a;
    }
    .uploader-list {
        position: absolute;
        width: 70%;
        left: 125px;
        top: 50%;
        transform: translateY(-50%);
        max-height: 400px;
        overflow: auto;
        overflow-x: hidden;
        overflow-y: auto;
        height: auto;
        /deep/.iconfont {
            font-size: 18px;
            color: #409eff;
        }
    }

}

//手机等小屏幕手持设备。当设备宽度  在  320px和768px之间时,执行当前的css
@media only screen and (min-width: 320px) and (max-width: 768px) {
    .uploader {
        width: 98%;
        padding: 0;
        box-shadow: none;
    }
}
</style>